#include "Dungeon.h"
#include <cstdlib>
#include <ctime>
#include <iostream>
#include <iomanip>
#include <conio.h>
#include <string>
#include <Windows.h>
#include <cmath>
using namespace std;

//Helper function to calulate absolute distance along one axis
int getDist(int a, int b){
	return abs(a - b);
}

Dungeon::Dungeon()
{
	exited = false;
	dead = false;
}


Dungeon::~Dungeon()
{
}

void Dungeon::genDungeon(int traps)
{
	srand(time(NULL));
	//Fill dungeon with empty space
	for (int i = 0; i < 10; i++){
		for (int j = 0; j < 10; j++){
			dungeon[i][j] = '.';
		}
	}
	//First  randomly place player then randomly place traps
	for (int i = 0; i < traps + 1; i++){
		int x = rand() % 10;
		int y = rand() % 10;
		if (i == 0){
			dungeon[x][y] = 'P';
			xpos = x;
			ypos = y;
		}
		else{
			//Make sure the trap is generated in an empty space
			if (dungeon[x][y] == '.'){
				dungeon[x][y] = 'T';
			}
		}
	}
	//Randomly generate the exit and place the monster
	bool exitPlaced = false, monsterPlaced = false;
	cout << "generating dungeon.";
	while (!(exitPlaced && monsterPlaced)){
		cout << "."; 
		int x = rand() % 10;
		int y = rand() % 10;
		//Generate new x values until the exit is atleast 5 steps away from the player along the x-axis
		while (getDist(x, xpos) < 5){
			x = rand() % 10;
			cout << "."; 
		}
		//Generate new y values until the exit is atleast 5 steps away from the player along the y-axis
		while (getDist(y, ypos) < 5){
			y = rand() % 10;
			cout << "."; 
		}
		//Make sure the exit/monster is placed in an empty space
		if (dungeon[x][y] == '.'){
			if (!exitPlaced){
				dungeon[x][y] = 'X';
				exitPlaced = true;
			}
			else{
				dungeon[x][y] = 'M';
				mxpos = x;
				mypos = y;
				monsterPlaced = true;
			}
		}
	}
}

void Dungeon::drawDungeon()
{
	HANDLE  hConsole;
	hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
	int color;
	system("cls");
	for (int i = 0; i < 10; i++){
		for (int j = 0; j < 10; j++){
			//Draw the player as green
			if (dungeon[i][j] == 'P'){
				color = 10;
			}
			//Draw traps as red
			else if (dungeon[i][j] == 'T'){
				color = 12;
			}
			//Draw the exit as lightblue
			else if (dungeon[i][j] == 'X'){
				color = 11;
			}
			//Draw the monster as yellow
			else if (dungeon[i][j] == 'M'){
				color = 14;
			}
			//Draw everything else white
			else{
				color = 15;
			}
			SetConsoleTextAttribute(hConsole, color);
			cout << " " << dungeon[i][j];
		}
		cout << endl << endl ;
	}
	color = 15;
	SetConsoleTextAttribute(hConsole, color);
	cout << "Press Esc to redraw dungeon.." << endl;
	//cout << endl << "X: " << xpos << " Y: " << ypos << endl << endl << endl; //Debugging output
}

bool Dungeon::movePlayer(int xChange, int yChange)
{
	bool newSpotOK = true;
	//Make the old spot empty
	dungeon[xpos][ypos] = '.';
	//Check if the player moves into a trap or a monster
	if (dungeon[xpos + xChange][ypos + yChange] == 'T' || dungeon[xpos + xChange][ypos + yChange] == 'M'){
		dead = true;
		newSpotOK = false;
	}
	//Check if the player moves into the exit
	else if (dungeon[xpos + xChange][ypos + yChange] == 'X'){
		exited = true;
		newSpotOK = false;
	}
	//Move the player in the dungeon
	dungeon[xpos + xChange][ypos + yChange] = 'P';
	xpos += xChange;
	ypos += yChange;
	return newSpotOK;
}

bool Dungeon::playerTurn()
{
	int xChange = 0, yChange = 0;
	int key = _getch();
	//Move left
	if (key == 75 && ypos > 0){
		yChange = -1;;
	}
	//Move up
	else if (key == 72 && xpos > 0){
		xChange = -1;
	}
	//Move right
	else if (key == 77 && ypos < 9){
		yChange = 1;
	}
	//Move down
	else if (key == 80 && xpos < 9){
		xChange = 1;
	}
	//Escape and redraw the dungeon
	else if (key == 27){
		return false;
	}
	return movePlayer(xChange, yChange);
}

bool Dungeon::moveMonster(int xChange, int yChange)
{
	//Make the old spot empty
	dungeon[mxpos][mypos] = '.';
	//Move the player in the dungeon
	if (dungeon[mxpos + xChange][mypos + yChange] == 'P'){
		dead = true;
	}
	dungeon[mxpos + xChange][mypos + yChange] = 'M';
	mxpos += xChange;
	mypos += yChange;
	return dead;
}

bool Dungeon::monsterAI()
{
	bool monsterMoved = false;
	int xChange = 0, yChange = 0;
	while (!monsterMoved){
		//Randomly choose a direction to move in and check that it's a valid position
		int moveDir = rand() % 4;
		if (moveDir == 0 && mxpos > 0 && (dungeon[mxpos - 1][mypos] == '.' || dungeon[mxpos - 1][mypos] == 'P')){
			xChange = -1;
			monsterMoved = true;
		}
		else if (moveDir == 1 && mxpos < 9 && (dungeon[mxpos + 1][mypos] == '.' || dungeon[mxpos + 1][mypos] == 'P')){
			xChange = 1;
			monsterMoved = true;
		}
		else if (moveDir == 2 && mypos > 0 && (dungeon[mxpos][mypos - 1] == '.' || dungeon[mxpos][mypos - 1] == 'P')){
			yChange = -1;
			monsterMoved = true;
		}
		else if (moveDir == 0 && mypos < 9 && (dungeon[mxpos][mypos + 1] == '.' || dungeon[mxpos][mypos + 1] == 'P')){
			yChange = 1;
			monsterMoved = true;
		}
	}
	return moveMonster(xChange, yChange);
}

void Dungeon::resetDungeon(){
	dead = false;
	exited = false;
}

void Dungeon::initDungeon()
{
	system("cls");
	cout << "How many traps should be generated? ";
	cin >> numTraps;
	//check if in valid range
	if (numTraps <0 || numTraps >50){
		cout << numTraps << " is not in the valid range for number of traps.." << endl << "Please select a number in the range 0-50 \n";
		system("pause");
		initDungeon();
	}
}

bool Dungeon::endScreen()
{
	string endText;
	if (dead || exited){
		if (dead){
			endText = "You died.. Better luck next time";
		}
		//Exited
		else{
			endText = "Congratulations, you survived!";
		}
		cout << endText << endl;
		cout << "Press any key to play again and use Esc to exit.." << endl;
		//Close the game
		if (_getch() == 27){
			return false;
		}
		resetDungeon();
		initDungeon();
		return true;
	}
	//Exited with escape to redraw the dungeon
	else{
		return true;
	}
}

void Dungeon::runGame()
{
	initDungeon();
	do{
		genDungeon(numTraps);
		drawDungeon();
		//Let the player control the character as long as the moves are valid and keep redrawing after every move
		while (playerTurn()){
			drawDungeon();
			if (monsterAI()){
				break;
			}
		}
		drawDungeon();
		//Exited through death or level completion
	} while (endScreen());
}